#light

(* Create a schema and actual graph description.  *)


module CGraph.DataSchema
open CGraph.Generic
open CGraph.DG2
open CGraph.Generic.Impl
open CGraph.Generic.Algorithms
open CGraph.Generic
open CGraph.DataModel
open CGraph.IO


//Need typed reference, list of typed reference.
(*
    - Complex vertexes can have properties, and may derive
        from other complex vertexes.
    - Data Vertexes are represented by datatypes in the data model.
    - property edges may either point to a data vertex, or one of
        the two reference vertexes.
    - You can either reference a single object, or a list of objects.
    - The reference edges point to the possible datatypes for the reference.
    - Complex edges point to complex types defined for the scope
        of the given complex edge and below.
    - Conditionals are meant to indicate properties that may or may
        not exist based on some factor.  They cannot be completely
        described in general without the runtime system as they require
        runtime information in order to make work.  They are used as
        switches; should they evaluate to true, then the properties
        they speak of are active, else none are.  Thus if you have
        properties on both sides of a branch, then you need two
        conditionals, one for false and one for true.
    - The data needs to be very richly typed.  Thus you will have the
    core datatypes and then you will have a set of attributes per datatype.
    The mixture of datatype to attribute list will be called a data
    description.
*)

type ComplexVertex(m_name:SymbolHandle) =
    inherit Vertex()
    member s.Name = m_name
    interface ISymboledObject with
        member s.Symbol = m_name
    
type ComplexVertexHandle = Handle<ComplexVertex>

type DataVertex(m_name:SymbolHandle,m_semantic:SymbolHandle,m_default:DataModelDataEntryHandle) = 
    inherit Vertex()
    member s.Name = m_name
    member s.Semantic = m_semantic
    member s.Default = m_default
    interface ISymboledObject with
        member s.Symbol = m_name
    
type DataVertexHandle = Handle<DataVertex>

type PropertyVertex(m_name:SymbolHandle) =
    inherit Vertex()
    interface ISymboledObject with
        member s.Symbol = m_name
    member s.Name = m_name
   
type PropertyVertexHandle = Handle<PropertyVertex>

type ReferenceVertex() = 
    inherit Vertex()
    
type ReferenceVertexHandle = Handle<ReferenceVertex>

type ReferenceListVertex() = inherit Vertex()
    
type ReferenceListVertexHandle = Handle<ReferenceListVertex>

type ReferenceEdge(inc,out) = inherit Edge(inc,out)
    
type ReferenceEdgeHandle = Handle<ReferenceEdge>

type PropertyEdge(inc,out) = inherit Edge(inc,out)
    
type PropertyEdgeHandle = Handle<PropertyEdge>

type DatatypeEdge(inc,out) = inherit Edge(inc,out)
    
type DatatypeEdgeHandle = Handle<DatatypeEdge>

type ConditionalInput(inc,out) = inherit Edge(inc,out)
    
type ConditionalInputHandle = Handle<ConditionalInput>

type ConditionalEdge(inc,out) = inherit Edge(inc,out)
    
type ConditionalEdgeHandle = Handle<ConditionalEdge>

type DerivationEdge(inc,out) = inherit Edge(inc,out)

type DerivationEdgeHandle = DerivationEdge Handle
    
//Theoretically you could need many types of conditionals.  In reality, however,
//you usually never need very many.
type Conditional =
    | EqualConditional of obj
   
type InternalConditional = 
    | EqualConditional of DataModelDataEntryHandle
    
type ConditionalVertex(m_conditional:InternalConditional) = 
    inherit Vertex()
    member s.Conditional = m_conditional
    
type ConditionalVertexHandle = Handle<ConditionalVertex>
    
type ConditionalOrComp =
    | CompVert of ComplexVertex
    | CondVert of ConditionalVertex
    
type ConditionalOrCompHandle =
    | ConditionalHandle of ConditionalVertexHandle
    | ComplexHandle of ComplexVertexHandle
    
let CreateConditionaOrCompHandle (hdl:int) (vert:Vertex) =
    match vert with
        | :? ConditionalVertex as vert-> ConditionalHandle (ToHandle vert hdl)
        | :? ComplexVertex as vert -> ComplexHandle (ToHandle vert hdl)
        | _ -> failwith("Incorrect arg")

let ToConditionalOrComp (hdl:ConditionalOrCompHandle) (lookup:int->obj) =
    match hdl with
        | ConditionalHandle(hdl) -> CondVert(lookup(hdl.Handle) :?> ConditionalVertex)
        | ComplexHandle(hdl) -> CompVert(lookup(hdl.Handle) :?> ComplexVertex)
    
type SchemaGraphNameable = 
    | ComplexHandle of ComplexVertexHandle
    | PropertyVertex of PropertyVertexHandle
    
type  PropertyTypeHandle = 
    | PropertyVertex of PropertyVertexHandle
    | ConditionalVertex of ConditionalVertexHandle
    
type PropertyDatatypeHandle = 
    | SimpleDatatype of string*string*obj
    | ComplexDatatype of ComplexVertexHandle seq
    | ComplexListDatatype of ComplexVertexHandle seq
    
type SimplePropertyHandle = PropertyVertexHandle*DataVertexHandle
    
        
(*What general operations does an ISchemaGraph fullfill?  
At this point you need only a couple things. *)
type ISchemaGraph =
    //Creation
    abstract Complex : name:string -> ComplexVertexHandle
    abstract DataProperty : parent:ConditionalOrCompHandle*name:string*semantic:string*defVal:obj -> PropertyVertexHandle
    abstract ComplexProperty : name:string*parent:ConditionalOrCompHandle*data:ComplexVertexHandle seq-> PropertyVertexHandle
    abstract ComplexListProperty : name:string*parent:ConditionalOrCompHandle*data:ComplexVertexHandle seq -> PropertyVertexHandle
    //Derivation simply inserts the properties of the superclass into the sub class.
    //Really, you could look at this as adding a property group.
    abstract Derive: super:ComplexVertexHandle*sub:ComplexVertexHandle -> DerivationEdgeHandle
    //This should make it clear that you can only build conditional properties on simple datatypes.  
    //Technically, complex datatypes do not have values.
    //This will be checked internal to the method.
    abstract Conditional : comp:ConditionalOrCompHandle*input:PropertyVertexHandle seq*conditional:Conditional->ConditionalVertexHandle
    
    //This is used when you want a several conditionals that share the same property vertex.  Sometimes
    //a,b both imply d but c does not.  In that case, build a->c and then simply call ConditionalOutput after
    //creating the b conditional and not hooking properties up to it.
    abstract ConditionalOutput : cond:ConditionalVertexHandle*output:PropertyVertexHandle -> unit
    
    //These are the complex vertexes that are *not* referenced through properties.
    abstract ComplexVertexes : ComplexVertexHandle seq
    abstract Name : hdl:SchemaGraphNameable -> string
    abstract Properties : hdl:ConditionalOrCompHandle -> PropertyTypeHandle seq
    abstract PropertyDatatype : hdl:PropertyVertexHandle -> PropertyDatatypeHandle
    //Call properties to get the outputs.
    abstract ConditionalInputs : hdl:ConditionalVertexHandle -> SimplePropertyHandle seq
    abstract ConditionalData : hdl:ConditionalVertexHandle -> Conditional
    

//Reverse map maps to and from the graph handles to my specific objects.
type SchemaGraphData = { Graph:IDirectedGraph; HandleSystem:ObjectHandleSystem; Data:IDataModel; 
    Symbols:ISymbolTable; BackingStore:DirectedGraphData }
    
let CreateSchemaGraphData model symbols incFun =
    let graphData = CreateDirectedGraphData incFun
    let graph = CreateDirectedGraph graphData  
    
    //Note that I use the same handle system for both the graph
    //and my ischema graph.  This is very important; the implementations
    //below depend on this.
    { Graph=graph; HandleSystem=graphData.HandleSystem; Data=model; Symbols=symbols; BackingStore=graphData }
    
    
let CreateISchemaGraph (graphData:SchemaGraphData) = 
    let ToHandle arg = graphData.HandleSystem.ToHandle arg
    let FromHandle arg= graphData.HandleSystem.FromHandle arg
    let FromHandleSeq values = graphData.HandleSystem.FromHandleSeq values
    let ToHandleSeq values = graphData.HandleSystem.ToHandleSeq values
    
    let ToConditionalOrComp hdl = ToConditionalOrComp hdl graphData.HandleSystem.GetHandleObj
    
    let ToVertexHandle (hdl:#Vertex Handle) = Handle<Vertex>(hdl.Handle)
    let ToEdgeHandle (hdl:#Edge Handle) = Handle<Edge>(hdl.Handle)
        
    let addv (vert:#Vertex) = 
        graphData.Graph.AddVertex (ToHandle (vert :> Vertex))
        ToHandle vert
        
    let adde (edge:#Edge) =
        graphData.Graph.AddEdge (ToHandle (edge :> Edge))
        ToHandle edge
    
    
    let ToVertexHandleCondOrComp (comp:ConditionalOrCompHandle) =
        match comp with
            | ConditionalOrCompHandle.ComplexHandle(hdl) -> ToVertexHandle(hdl)
            | ConditionalOrCompHandle.ConditionalHandle(hdl) -> ToVertexHandle(hdl)
    
    let PropertyDatatype (pv:PropertyVertex) = 
        pv.OutgoingEdges
        |> Seq.first( fun(edge) -> 
            match (FromHandle edge) with
                | :? DatatypeEdge as pdv -> Some (pdv.OutgoingVertex)
                | _ -> None )
    
    let ToSimpleProperty (pv:PropertyVertex) =
        let datatype = 
            match PropertyDatatype pv with
                | Some vert -> (FromHandle vert) :?> DataVertex
                | None -> failwith "Not a simple property"
        (ToHandle pv),(ToHandle datatype)
        
    let ToProperty name comp dtype =
        let dtype = ToVertexHandle dtype
        let newv = addv (new PropertyVertex(name))
        let parent = ToVertexHandleCondOrComp comp
        let newe = adde (new PropertyEdge(parent,(ToVertexHandle newv)))
        let dedge = adde (DatatypeEdge((ToVertexHandle newv),dtype))
        newv
        
    let AddRefList refv data = 
        let vhdl = ToVertexHandle refv
        data |> Seq.iter(fun(entry)->
            adde (ReferenceEdge(vhdl,(ToVertexHandle entry))) |> ignore )
            
    let AddReferenceProperty name parent refv data = 
        AddRefList refv data
        ToProperty name parent refv
    
    let Intern name = graphData.Symbols.Symbol name
    let SymbolName sym = graphData.Symbols.Name sym
      
    { new ISchemaGraph with
        member s.Complex name = addv (ComplexVertex (Intern name))
        member s.DataProperty (parent,name,semantic,defVal) = 
            let defVal = graphData.Data.Create( semantic, defVal );
            let dv = addv(DataVertex((Intern name),(Intern semantic),defVal))
            ToProperty (Intern name) parent dv
            
        member s.ComplexProperty (name,parent,data) = AddReferenceProperty (Intern name) parent (addv (ReferenceVertex())) data
        member s.ComplexListProperty (name,parent,data) = AddReferenceProperty (Intern name) parent (addv (ReferenceListVertex())) data
        //Derivation points from the sub-class to the super class.  Thus to see who derives from a superclass,
        //you would need to scan incoming edges.
        member s.Derive (super,sub) = adde (DerivationEdge((ToVertexHandle sub),(ToVertexHandle super)))
        member s.Conditional (comp,input,conditional) =
            let AssertSimpleProperty (entry:PropertyVertexHandle) =
                ToSimpleProperty (FromHandle entry) |> ignore
            let props = 
                input
                |> Seq.map( fun(entry) ->
                    AssertSimpleProperty entry
                    entry )
                |> FromHandleSeq
            let iternCond = 
                match conditional with
                    | Conditional.EqualConditional(arg) -> InternalConditional.EqualConditional(graphData.Data.Create("__unknown__",arg))
            let newv = addv (new ConditionalVertex(iternCond))
            input |> Seq.iter(fun entry -> (adde (ConditionalInput((ToVertexHandle newv),(ToVertexHandle entry)))) |> ignore )
            let parent = ToVertexHandleCondOrComp comp
            adde (ConditionalEdge(parent,(ToVertexHandle newv))) |> ignore
            newv
            
        member s.ConditionalOutput (cond,output) =
            adde (new PropertyEdge((ToVertexHandle cond),(ToVertexHandle output))) |> ignore
            
        member s.ComplexVertexes =
            graphData.Graph.Vertexes
            |> Seq.filter( fun(vert)->
                if ( (FromHandle vert) :? ComplexVertex) then true else false )
            |> Seq.map( fun(vert)-> Handle<ComplexVertex>(vert.Handle))
            
        member s.Name hdl =
            let nameable = 
                match hdl with
                    | SchemaGraphNameable.ComplexHandle(hdl) -> (FromHandle hdl) :> ISymboledObject
                    | SchemaGraphNameable.PropertyVertex(hdl) -> (FromHandle hdl) :> ISymboledObject
                    
            SymbolName <| nameable.Symbol
            
        member s.Properties hdl = 
            let parent = ToVertexHandleCondOrComp hdl
            let rec ListProperties (parent:VertexHandle) = 
                seq {
                    for edge in (FromHandle parent).OutgoingEdges do
                        match (FromHandle edge) with
                            | :? PropertyEdge as edge -> 
                                let vert = (FromHandle edge.OutgoingVertex) :?> PropertyVertex
                                yield PropertyVertex(ToHandle vert)
                            | :? ConditionalEdge as edge ->
                                let vert = (FromHandle edge.OutgoingVertex) :?> ConditionalVertex
                                yield ConditionalVertex(ToHandle vert)
                            | :? DerivationEdge as edge ->
                                let vert = (FromHandle edge.OutgoingVertex):?> ComplexVertex
                                yield! ListProperties (ToVertexHandle (ToHandle vert))
                            | _ -> yield! []
                }
            ListProperties parent
                        
        member s.PropertyDatatype hdl =
            let pv = FromHandle hdl
                        
            let complexReferences (vert:Vertex) = 
                vert.OutgoingEdges
                |> FromHandleSeq
                |> Seq.filter( fun edge -> edge :? ReferenceEdge)
                |> Seq.map( fun edge -> 
                    let vtx = FromHandle edge.OutgoingVertex 
                    ToHandle (vtx :?> ComplexVertex))
                    
            let dtype = PropertyDatatype pv       
            match dtype with
                | Some vert ->
                    match (FromHandle vert) with
                        | :? DataVertex as dv -> SimpleDatatype ((SymbolName (dv :> ISymboledObject).Symbol),(SymbolName dv.Semantic),graphData.Data.GetData( dv.Default ) )
                        | :? ReferenceVertex as rv -> ComplexDatatype (complexReferences (rv :> Vertex))
                        | :? ReferenceListVertex as rlv -> ComplexListDatatype (complexReferences (rlv :> Vertex))
                        | _ -> failwith "programmer error"
                | None -> failwith "Property with a datatype?"
                
        member s.ConditionalInputs hdl = 
            let cond = FromHandle hdl
            cond.OutgoingEdges
                |> FromHandleSeq
                |> Seq.filter( fun(edge)-> edge :? ConditionalInput )
                |> Seq.map( fun edge -> ToSimpleProperty ((FromHandle edge.OutgoingVertex) :?> PropertyVertex) )
                
        member s.ConditionalData hdl =
            let cond = FromHandle hdl
            match cond.Conditional with
                | EqualConditional(arg) -> 
                    let model = graphData.Data
                    Conditional.EqualConditional(model.GetData arg)
        }
        
type SchemaGraphSerializationType =
    | Unknown = 0
    | ComplexVertex = 1
    | DataVertex = 2
    | PropertyVertex = 3
    | ReferenceVertex = 4
    | ReferenceListVertex = 5
    | ReferenceEdge = 6
    | PropertyEdge = 7
    | DatatypeEdge = 8
    | ConditionalInput = 9
    | ConditionalEdge = 10
    | DerivationEdge = 11
    | EqualConditional = 12
    | ConditionalVertex = 13
    
let WriteSchemaGraph (data:SchemaGraphData) (writer:IDataWriter) =
    let vertWriter (item:Vertex) (output:int->unit) = 
        match item with
            | :? ComplexVertex as vert -> 
                output( int(SchemaGraphSerializationType.ComplexVertex) )
                output  vert.Name.Handle
            | :? DataVertex as vert ->
                output (int SchemaGraphSerializationType.DataVertex )
                output vert.Name.Handle
                output vert.Semantic.Handle
                output vert.Default.Handle
            | :? PropertyVertex as vert ->
                output (int SchemaGraphSerializationType.PropertyVertex )
                output vert.Name.Handle
            | :? ReferenceVertex as vert ->
                output (int SchemaGraphSerializationType.ReferenceVertex )
            | :? ReferenceListVertex as vert ->
                output (int SchemaGraphSerializationType.ReferenceListVertex )
            | :? ConditionalVertex as vert ->
                output (int SchemaGraphSerializationType.ConditionalVertex)
                match vert.Conditional with
                    | EqualConditional(cond) -> 
                        output (int SchemaGraphSerializationType.EqualConditional)
                        output cond.Handle 
            | _ -> failwith "Unrecognized vertex type"
            
    let edgeWriter (edge:Edge) (output:int->unit) = 
        match edge with
            | :? ReferenceEdge as edge -> output (int SchemaGraphSerializationType.ReferenceEdge)
            | :? PropertyEdge as edge -> output (int SchemaGraphSerializationType.PropertyEdge)
            | :? DatatypeEdge as edge -> output (int SchemaGraphSerializationType.DatatypeEdge)
            | :? ConditionalInput as edge -> output (int SchemaGraphSerializationType.ConditionalInput)
            | :? ConditionalEdge as edge -> output (int SchemaGraphSerializationType.ConditionalEdge)
            | :? DerivationEdge  as edge -> output (int SchemaGraphSerializationType.DerivationEdge)
            | _ -> failwith "Unrecoginzed edge type"
    WriteDirectedGraphData data.BackingStore writer vertWriter edgeWriter
    
type EdgeCallback = VertexHandle->VertexHandle->Edge
    
let ReadSchemaGraph (data:ObjectHandleSystem) (reader:IDataReader) =
    let storeObj hdl arg = data.StoreObject hdl (arg :> obj)
    let readVert (vhandle:int) (input:unit->int) =
        let storeObj arg = 
            storeObj vhandle arg
            arg :> Vertex
        let nextSymbol() = Handle<Symbol>(input())
        let nextData() = Handle<DataModelDataEntry>(input())
        let (dtype:SchemaGraphSerializationType) = enum(input())
        match dtype with
            | SchemaGraphSerializationType.ComplexVertex -> 
                let sym = nextSymbol()
                let cvert = ComplexVertex(sym)
                storeObj cvert
            | SchemaGraphSerializationType.DataVertex -> 
                let name = nextSymbol()
                let semantic = nextSymbol()
                let defVal = nextData()
                let vert = DataVertex(name,semantic,defVal)
                storeObj vert
            | SchemaGraphSerializationType.PropertyVertex -> 
                let name= nextSymbol()
                storeObj <| new PropertyVertex(name)
            | SchemaGraphSerializationType.ReferenceVertex -> storeObj <| ReferenceVertex()
            | SchemaGraphSerializationType.ReferenceListVertex -> storeObj <| ReferenceListVertex()
            | SchemaGraphSerializationType.ConditionalVertex -> 
                let (condType:SchemaGraphSerializationType) = enum(input())
                let cond = 
                    match condType with
                        | SchemaGraphSerializationType.EqualConditional -> 
                            let data = nextData()
                            InternalConditional.EqualConditional(data)
                        | _ -> failwith "Unhandled shizzle"
                storeObj <| new ConditionalVertex(cond)
            | _ -> failwith "Unrecognized vertex type"
            
    let readEdge (ehandle:int) (inc:VertexHandle) (out:VertexHandle) (input:unit->int) =
        let storeObj arg = 
            storeObj ehandle arg
            arg :> Edge
        let (etype:SchemaGraphSerializationType) = enum(input())
        match etype with
            | SchemaGraphSerializationType.ReferenceEdge -> storeObj <| ReferenceEdge(inc,out) 
            | SchemaGraphSerializationType.DatatypeEdge -> storeObj <| DatatypeEdge(inc,out) 
            | SchemaGraphSerializationType.PropertyEdge -> storeObj <| PropertyEdge(inc,out) 
            | SchemaGraphSerializationType.ConditionalInput -> storeObj <| ConditionalInput(inc,out)
            | SchemaGraphSerializationType.ConditionalEdge -> storeObj <| ConditionalEdge(inc,out)
            | SchemaGraphSerializationType.DerivationEdge -> storeObj <| DerivationEdge(inc,out)
            | _ -> failwith "Unrecognized edge type"
    ReadDirectedGraphData reader readVert readEdge